﻿using System;
using UnityEditor;
using UnityEngine;

namespace ZVertexTool
{
	public class VertexPainter : EditorWindow {
		//Standart Editor Variables//
		private static VertexPainter window;

		[MenuItem("Window/Tools/Vertex Painter")]
		private static void CreateWindow() {
			//Init window
			int x = 150, y = 150, w = 320, h = 150;
			window = (VertexPainter)GetWindow(typeof(VertexPainter), false);
			window.position = new Rect(x, y, w+x, h+y);
			window.minSize = new Vector2(w, h);
			window.maxSize = new Vector2(w+100, h+100);
			window.title = "Vertex Paint";
			window.Show();
		}

	    private DebugColorChannel curChannel = DebugColorChannel.RGBA;
		//Variables//
		private GameObject go;
		private Collider collider;
		private MeshFilter mf;
		private Mesh mesh;
		private Renderer renderer;
		private Vector3[] vertices;
		private Color[] originalColors, debugColors;
		private Material originalMaterial;
		private static Material debugMaterial;

		//GUI Variables//
		private bool tgl_Paint;
		private string str_Paint;
		private bool tgl_ShowVertexColors;
		private string str_ShowVertexColors;
		private Color gui_BrushColor;
		private float gui_BrushSize;
		private float gui_BrushOpacity;
		private string gui_Notification;
		private bool canPaint;

		//Standart Mono Methods//
		void OnEnable() {
			if(SceneView.onSceneGUIDelegate != OnSceneGUI)
				SceneView.onSceneGUIDelegate += OnSceneGUI;
			//Create debug material
			if(debugMaterial == null) {
				debugMaterial = new Material(Shader.Find("TOZ/Debug/VertexColors"));
			}
			ResetMe();
		}

		void OnDisable() {
			if(SceneView.onSceneGUIDelegate == OnSceneGUI)
				SceneView.onSceneGUIDelegate -= OnSceneGUI;
			//Cleanup
			DestroyImmediate(debugMaterial);
			ResetMe();
		}

		void OnSelectionChange() {
			ResetMe();
			window.Repaint();
		}

		void OnProjectChange() {
			ResetMe();
			window.Repaint();
		}

		void OnInspectorUpdate() {
			this.Repaint();
		}

		void OnGUI() {
			//Warnings
			if(!canPaint) {
				EditorGUILayout.HelpBox(gui_Notification, MessageType.Warning);
				return;
			}
			//We can paint now
			EditorGUILayout.BeginVertical("box");
			EditorGUILayout.BeginHorizontal();
			if(GUILayout.Button(str_Paint)) {
				tgl_Paint = !tgl_Paint;
				if(tgl_Paint) {
					str_Paint = "STOP PAINTING";
					//Debug Material
					renderer.sharedMaterial = debugMaterial;
					//Other button
					tgl_ShowVertexColors = true;
					str_ShowVertexColors = "HIDE VERTEX COLORS";
				}
				else {
					str_Paint = "START PAINTING";
					ResetMe();
				}
			}

			if(GUILayout.Button(str_ShowVertexColors)) {
				tgl_ShowVertexColors = !tgl_ShowVertexColors;
				if(tgl_ShowVertexColors) {
					str_ShowVertexColors = "HIDE VERTEX COLORS";
					//Debug Material
					renderer.sharedMaterial = debugMaterial;
				}
				else {
					str_ShowVertexColors = "SHOW VERTEX COLORS";
					renderer.sharedMaterial = originalMaterial;
				}
			}
			EditorGUILayout.EndHorizontal();

			if(tgl_Paint) {
                #region 笔刷颜色
                //Top
                EditorGUILayout.Space();
				EditorGUILayout.BeginVertical();
				gui_BrushSize = EditorGUILayout.Slider("Brush Size :", gui_BrushSize, 0.1f, 10.0f);
				gui_BrushOpacity = EditorGUILayout.Slider("Brush Opacity :", gui_BrushOpacity, 0.0f, 1.0f);
				EditorGUILayout.BeginHorizontal();
				EditorGUILayout.PrefixLabel("Brush Color :");
				if(GUILayout.Button("R", GUILayout.ExpandWidth(false))) {
					gui_BrushColor = new Color(1f, 0f, 0f, 0f);
				}
				if(GUILayout.Button("G", GUILayout.ExpandWidth(false))) {
					gui_BrushColor = new Color(0f, 1f, 0f, 0f);
				}
				if(GUILayout.Button("B", GUILayout.ExpandWidth(false))) {
					gui_BrushColor = new Color(0f, 0f, 1f, 0f);
				}
				if(GUILayout.Button("A", GUILayout.ExpandWidth(false))) {
					gui_BrushColor = new Color(0f, 0f, 0f, 1f);
				}
				gui_BrushColor = EditorGUILayout.ColorField(gui_BrushColor, GUILayout.Height(20));
			    if (GUILayout.Button("铺满", GUILayout.ExpandWidth(false)))
			    {
			        for (int i = 0; i < vertices.Length; i++)
			        {
			            debugColors[i] = gui_BrushColor;
			        }
			        mesh.colors = debugColors;
			        EditorUtility.SetDirty(go);
			    }
                EditorGUILayout.EndHorizontal();
				EditorGUILayout.EndVertical();
                #endregion

                //Center
                #region 设置默认颜色

                EditorGUILayout.Space();
			    EditorGUILayout.BeginHorizontal();
			    EditorGUILayout.PrefixLabel("通道查看 :");
			    DrawShowDebugColor();


                if (GUILayout.Button("RESET"))
			    {
			        mesh.colors = originalColors;
			        EditorUtility.SetDirty(go);
			    }
			    EditorGUILayout.EndHorizontal();

			    #endregion

                //Bottom
                EditorGUILayout.Space();
				if(GUILayout.Button("SAVE NEW MESH"))
				{
				    var path = EditorUtility.SaveFilePanelInProject("文件保存", "New Mesh", "mat","保存文件中");
                    //Create an instance
                    SaveMesh(mesh, path);
                    //Revert original mesh colors
                    mesh.colors = originalColors;
					EditorUtility.FocusProjectWindow();
					//window.Close();
				}
			}
			EditorGUILayout.EndVertical();
		}

		void OnSceneGUI(SceneView sceneView) {
			if(!tgl_Paint) {
				return;
			}

			Event current = Event.current;
			Ray ray = HandleUtility.GUIPointToWorldRay(current.mousePosition);
			RaycastHit hit;
			//Events
			int controlID = GUIUtility.GetControlID(sceneView.GetHashCode(), FocusType.Passive);
			switch(current.GetTypeForControl(controlID)) {
				case EventType.Layout:
				{
					if(!tgl_Paint) {
						return;
					}
					HandleUtility.AddDefaultControl(controlID);
				}
				break;
				case EventType.MouseDown:
				case EventType.MouseDrag:
				{
					if(current.GetTypeForControl(controlID) == EventType.MouseDrag && GUIUtility.hotControl != controlID) {
						return;
					}
					if(current.alt || current.control) {
						return;
					}
					if(current.button != 0) {
						return;
					}
					if(!tgl_Paint) {
						return;
					}
					if(HandleUtility.nearestControl != controlID) {
						return;
					}
					if(current.type == EventType.MouseDown) {
						GUIUtility.hotControl = controlID;
					}
					//Do painting
					if(Physics.Raycast(ray, out hit, float.MaxValue)) {
						if(hit.transform == go.transform) {
							Vector3 hitPos = Vector3.Scale(go.transform.InverseTransformPoint(hit.point), go.transform.localScale);
							for(int i=0; i < vertices.Length; i++) {
								Vector3 vertPos = Vector3.Scale(vertices[i], go.transform.localScale);
								float mag = (vertPos - hitPos).magnitude;
								if(mag > gui_BrushSize)
									continue;
							    var srcColor = mesh.colors[i];
							    if (curChannel == DebugColorChannel.R)
							    {
							        debugColors[i] = Color.Lerp(debugColors[i],
							            new Color(gui_BrushColor.r, srcColor.g, srcColor.b, srcColor.a), gui_BrushOpacity);
							    }
                                else if (curChannel == DebugColorChannel.G)
							    {
							        debugColors[i] = Color.Lerp(debugColors[i],
							            new Color(srcColor.r, gui_BrushColor.g, srcColor.b, srcColor.a), gui_BrushOpacity);
							    }
							    else if (curChannel == DebugColorChannel.B)
							    {
							        debugColors[i] = Color.Lerp(debugColors[i],new Color(srcColor.r, srcColor.g,gui_BrushColor.b, srcColor.a) , gui_BrushOpacity);
							    }
							    else if (curChannel == DebugColorChannel.RGBA)
							    {
							        debugColors[i] = Color.Lerp(debugColors[i], gui_BrushColor, gui_BrushOpacity);
                                }
                                
							}
							mesh.colors = debugColors;
						}
					}
					current.Use();
				}
				break;
				case EventType.MouseUp:
				{
					if(!tgl_Paint) {
						return;
					}
					if(GUIUtility.hotControl != controlID) {
						return;
					}
					GUIUtility.hotControl = 0;
					current.Use();
				}
				break;
				case EventType.Repaint:
				{
					//Draw paint brush
					if(Physics.Raycast(ray, out hit, float.MaxValue)) {
						if(hit.transform == go.transform) {
							Handles.color = new Color(gui_BrushColor.r, gui_BrushColor.g, gui_BrushColor.b, 1.0f);
							Handles.DrawWireDisc(hit.point, hit.normal, gui_BrushSize);
						}
					}
					HandleUtility.Repaint();
				}
				break;
			}
		}

		//Private Methods//
		private void ResetMe() {
			//Reset previously worked on object if any
			if(go && originalMaterial) {
				go.GetComponent<Renderer>().sharedMaterial = originalMaterial;
				mesh.colors = originalColors;
			}

			//Reset variables
			go = null;
			collider = null;
			mf = null;
			mesh = null;
			renderer = null;
			vertices = null;
			originalColors = null;
			debugColors = null;
			originalMaterial = null;

			//Reset gui variables
			tgl_Paint = false;
			str_Paint = "START PAINTING";
			tgl_ShowVertexColors = false;
			str_ShowVertexColors = "SHOW VERTEX COLORS";
			gui_BrushColor = new Color(1f, 0f, 0f, 0f);
			gui_BrushSize = 1.0f;
			gui_BrushOpacity = 0.5f;
			canPaint = false;


            SetDebugColor(DebugColorChannel.RGBA);
            //Reset Selection
		    InitNewSelectGameObject();

		}

        /// <summary>
        /// 初始化新选择的模型
        /// </summary>
	    private void InitNewSelectGameObject()
	    {
	        go = Selection.activeGameObject;
	        if (go == null)
	        {
	            gui_Notification = "No object selected!";
	            return;
	        }

	        collider = go.GetComponent<Collider>();
	        if (collider == null)
	        {
	            gui_Notification = "Object doesnt have a collider!";
                return;
	        }

	        mf = go.GetComponent<MeshFilter>();
	        if (mf == null)
	        {
	            gui_Notification = "Object doesnt have a MeshFilter!";
                return;
	        }

	        mesh = mf.sharedMesh;
	        if (mesh == null)
	        {
	            gui_Notification = "Object doesnt have a mesh!";
                return;
	        }

	        //Save originals
	        renderer = go.GetComponent<Renderer>();
	        originalMaterial = renderer.sharedMaterial;
	        originalColors = mesh.colors;
	        //Set Arrays
	        vertices = mesh.vertices;
	        if (mesh.colors.Length > 0)
	            debugColors = mesh.colors;
	        else
	        {
	            Debug.LogWarning("Mesh originally has no vertex color data!!");
	            debugColors = new Color[vertices.Length];
	        }
	        //All is okay, we can paint now
	        canPaint = true;
        }


	    enum DebugColorChannel
	    {
            RGBA,
            R,
            G,
            B,
            A,
	    }

	    void DrawShowDebugColor()
	    {
	        if (curChannel == DebugColorChannel.RGBA)
	            GUI.backgroundColor = Color.yellow;
	        if (GUILayout.Button("RGBA", GUILayout.ExpandWidth(false)))
	        {
	            SetDebugColor(DebugColorChannel.RGBA);
	        }
	        GUI.backgroundColor = Color.white;

	        if (curChannel == DebugColorChannel.R)
	            GUI.backgroundColor = Color.yellow;
            if (GUILayout.Button("R", GUILayout.ExpandWidth(false)))
	        {
	            SetDebugColor(DebugColorChannel.R);
	        }
	        GUI.backgroundColor = Color.white;

            if (curChannel == DebugColorChannel.G)
	            GUI.backgroundColor = Color.yellow;
            if (GUILayout.Button("G", GUILayout.ExpandWidth(false)))
	        {
	            SetDebugColor(DebugColorChannel.G);
	        }
	        GUI.backgroundColor = Color.white;

            if (curChannel == DebugColorChannel.B)
	            GUI.backgroundColor = Color.yellow;
            if (GUILayout.Button("B", GUILayout.ExpandWidth(false)))
	        {
	            SetDebugColor(DebugColorChannel.B);
	        }
	        GUI.backgroundColor = Color.white;

            if (curChannel == DebugColorChannel.A)
	            GUI.backgroundColor = Color.yellow;
            if (GUILayout.Button("A", GUILayout.ExpandWidth(false)))
	        {
	            SetDebugColor(DebugColorChannel.A);
	        }
	        GUI.backgroundColor = Color.white;
        }

        /// <summary>
        /// 设置显示该通道颜色
        /// </summary>
        /// <param name="channel"></param>
	    private void SetDebugColor(DebugColorChannel channel = DebugColorChannel.RGBA)
	    {
	        if (debugMaterial == null)
	            return;
	        curChannel = channel;

            switch (channel)
	        {
	            case DebugColorChannel.RGBA:
	                debugMaterial.DisableKeyword("R");
	                debugMaterial.DisableKeyword("G");
	                debugMaterial.DisableKeyword("B");
	                debugMaterial.DisableKeyword("A");

                    debugMaterial.EnableKeyword("RGBA");
	                gui_BrushColor = Color.white;
                    break;
	            case DebugColorChannel.R:
	                debugMaterial.DisableKeyword("RGBA");
	                debugMaterial.DisableKeyword("G");
	                debugMaterial.DisableKeyword("B");

                    debugMaterial.EnableKeyword("R");

	                gui_BrushColor = Color.red;
                    break;
	            case DebugColorChannel.G:
	                debugMaterial.DisableKeyword("RGBA");
	                debugMaterial.DisableKeyword("R");
	                debugMaterial.DisableKeyword("B");

                    debugMaterial.EnableKeyword("G");

	                gui_BrushColor = Color.green;
                    break;
	            case DebugColorChannel.B:
	                debugMaterial.DisableKeyword("RGBA");
	                debugMaterial.DisableKeyword("R");
	                debugMaterial.DisableKeyword("G");

                    debugMaterial.EnableKeyword("B");
	                gui_BrushColor = Color.blue;
                    break;
	            case DebugColorChannel.A:
	                debugMaterial.DisableKeyword("RGBA");
	                debugMaterial.DisableKeyword("R");
	                debugMaterial.DisableKeyword("G");
	                debugMaterial.DisableKeyword("B");

                    debugMaterial.EnableKeyword("A");
	                gui_BrushColor = new Color(0,0,0,1);
                    break;
	            default:
	                throw new ArgumentOutOfRangeException("channel", channel, null);
	        }
           
	    }

        /// <summary>
        /// 保存网格
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="path"></param>
	    private void SaveMesh(Mesh mesh,string path)
	    {
	        Mesh data = (Mesh)Instantiate(mesh);
	        //data.hideFlags = HideFlags.HideAndDontSave;
	       // string name = AssetDatabase.GenerateUniqueAssetPath(path);
	        AssetDatabase.CreateAsset(data, path);
	        AssetDatabase.SaveAssets();
	        AssetDatabase.Refresh();
	        Debug.LogWarning("Mesh is Saved as " + name + ".");
            Selection.activeObject = data;
        }
	}

}